Skip to content

ci: run the test matrix and docs build on every push and pull request - #7

Merged
EternalTime merged 1 commit into
mainfrom
fm/ci-pygd
Aug 19, 2026
Merged

ci: run the test matrix and docs build on every push and pull request#7
EternalTime merged 1 commit into
mainfrom
fm/ci-pygd

Conversation

@EternalTime

Copy link
Copy Markdown
Owner

Intent

Make GitHub run pyGD's tests automatically on every push and every pull request so nothing lands untested. Add a GitHub Actions workflow at .github/workflows/tests.yml that installs the library exactly the way the repository's own README instructions describe - using the 'test' extra (pip install -e ".[test]") rather than installing pytest by hand - runs pytest, and does so across the FULL range of Python versions the repo claims to support. That claim was read from pyproject.toml (requires-python >=3.8 plus classifiers for 3.8 through 3.14) and the README ('Requires Python 3.8+'), so the matrix is deliberately 3.8-3.14 rather than a convenient subset; deliberately NOT dropping any claimed version was an explicit requirement. A second job builds the manual with the 'docs' extra (pip install -e ".[docs]" then sphinx-build) so a broken documentation build is caught too. The workflow was required to stay readable and boring: only actions/checkout and actions/setup-python, a plain version matrix, no dependency caching, no third-party actions. fail-fast is false so one failing version does not hide the others. The docs job intentionally pins a single Python (3.12) since documentation only needs to build once. The published install recipe was verified to work as written on a clean environment, so no README fix was needed. Scope was constrained to adding only the workflow file - no other repository changes. The branch was pushed and the resulting run observed on GitHub: all seven matrix entries (3.8-3.14) and the docs job passed.

What Changed

  • Added .github/workflows/tests.yml, a GitHub Actions workflow triggered on every push and pull request.
  • The test job installs the library the way the README documents (pip install -e ".[test]") and runs pytest across a fail-fast: false matrix covering Python 3.8 through 3.14, the full range declared by pyproject.toml (requires-python >=3.8 plus classifiers) and the README.
  • A separate docs job pins Python 3.12, installs the docs extra, and runs sphinx-build -b html docs docs/_build/html so a broken manual build fails CI too. The workflow uses only actions/checkout and actions/setup-python, with no dependency caching or third-party actions.

No source, packaging, or documentation files were touched. The pipeline's Test phase located the real GitHub run for this commit (32224258403) and confirmed all 8 jobs green - the seven matrix entries plus docs - and reproduced both README install recipes in a clean environment. Review left three informational notes for later consideration: no explicit permissions: block, no concurrency group to cancel superseded runs, and the floating ubuntu-latest label being the eventual failure point for the end-of-life 3.8 leg.

Risk Assessment

✅ Low: The change adds a single 32-line CI workflow file with no library or product code touched, it satisfies every source-verifiable criterion in the intent, and the only observations are non-blocking hardening and future-fragility notes.

Testing

I validated this CI change against the surface an end user actually sees: the GitHub Actions run itself. A run for the exact target commit already existed and I confirmed it succeeded across all seven matrix entries (3.8-3.14) and the docs job, then pulled the real CI logs to prove each entry ran on its claimed interpreter with 5 tests passing and that every job installed via the declared extras rather than hand-installing pytest or sphinx. I separately reproduced both published README recipes verbatim in a fresh venv on a clean copy of the repo, and both succeeded, corroborating the intent's claim that the install instructions work as written. A parsing check confirmed every explicit constraint (full matrix matching the pyproject classifiers with nothing dropped, fail-fast false, docs pinned to 3.12, no caching, no third-party actions) and that the diff adds only the workflow file. Visual evidence is a screenshot of the run summary page showing the green checks; the log-based artifacts back it with the actual pytest and sphinx output. One gap worth stating plainly: the observed run was triggered by push, so the declared pull_request trigger is verified by inspection and by GitHub successfully parsing the workflow, but not yet by a live PR - that evidence appears once a PR is opened. No test failures, flakiness, or setup problems surfaced, and I removed the temporary venv and log caches created during testing.

Evidence: Per-job evidence extracted from the real CI logs

GitHub Actions run 32224258403 - workflow 'Tests' - EternalTime/pyGD branch fm/ci-pygd commit 69f2cec event: push conclusion: SUCCESS duration: 55s --- test (3.8) --- Python 3.8.18, pytest-8.3.5 5 passed in 8.79s --- test (3.9) --- Python 3.9.25, pytest-8.4.2 5 passed in 7.32s --- test (3.10) --- Python 3.10.20, pytest-9.1.1 5 passed in 4.11s --- test (3.11) --- Python 3.11.15, pytest-9.1.1 5 passed in 4.84s --- test (3.12) --- Python 3.12.13, pytest-9.1.1 5 passed in 7.41s --- test (3.13) --- Python 3.13.15, pytest-9.1.1 5 passed in 7.41s --- test (3.14) --- Python 3.14.7, pytest-9.1.1 5 passed in 7.00s --- docs --- Running Sphinx v9.1.0 build succeeded. Install recipe actually executed on every CI job: test (3.8) .. test (3.14) pip install -e ".[test]" (no hand-installed pytest) docs pip install -e ".[docs]" (no hand-installed sphinx)

GitHub Actions run 32224258403 - workflow 'Tests' - EternalTime/pyGD
branch fm/ci-pygd  commit 69f2cec  event: push  conclusion: SUCCESS  duration: 55s
https://github.com/EternalTime/pyGD/actions/runs/32224258403

Per-job evidence pulled from the real CI logs:

--- test (3.8) ---
  runtime:  Python 3.8.18, pytest-8.3.5
  pytest:   5 passed in 8.79s

--- test (3.9) ---
  runtime:  Python 3.9.25, pytest-8.4.2
  pytest:   5 passed in 7.32s

--- test (3.10) ---
  runtime:  Python 3.10.20, pytest-9.1.1
  pytest:   5 passed in 4.11s

--- test (3.11) ---
  runtime:  Python 3.11.15, pytest-9.1.1
  pytest:   5 passed in 4.84s

--- test (3.12) ---
  runtime:  Python 3.12.13, pytest-9.1.1
  pytest:   5 passed in 7.41s

--- test (3.13) ---
  runtime:  Python 3.13.15, pytest-9.1.1
  pytest:   5 passed in 7.41s

--- test (3.14) ---
  runtime:  Python 3.14.7, pytest-9.1.1
  pytest:   5 passed in 7.00s

--- docs ---
  sphinx:   Running Sphinx v9.1.0
  sphinx:   build succeeded.
  sphinx:   The HTML pages are in docs/_build/html

Install recipe actually executed on every CI job (grepped from the same logs):
  test (3.8) .. test (3.14)   pip install -e ".[test]"      (no hand-installed pytest)
  docs                        pip install -e ".[docs]"      (no hand-installed sphinx)
This matches the README's published "Tests" and "Documentation" recipes verbatim.
Evidence: Clean-environment run of the README 'Tests' recipe (pip install -e ".[test]" + pytest)

$ pip install -e ".[test]" Successfully installed ... pyGD-0.1.0 pytest-9.1.1 ... $ pytest = test session starts == platform darwin -- Python 3.14.5, pytest-9.1.1, pluggy-1.6.0 configfile: pyproject.toml collected 5 items tests/test_slice.py ..... [100%] == 5 passed in 4.03s ===

### Following README 'Tests' recipe verbatim on a clean environment
$ python3 -m venv .venv && source .venv/bin/activate
Python 3.14.5
$ python -m pip install --upgrade pip
      Successfully uninstalled pip-26.1.1
Successfully installed pip-26.2.1

$ pip install -e ".[test]"
  Building editable for pyGD (pyproject.toml): started
  Building editable for pyGD (pyproject.toml): finished with status 'done'
  Created wheel for pyGD: filename=pygd-0.1.0-0.editable-py3-none-any.whl size=5994 sha256=7a661a57d066746620f9c8c400b5e4b827bc9c300e125986c936058e33ce3f63
  Stored in directory: /private/var/folders/ym/vnmfjh5n7dn2vdzdgy7zrbjh0000gn/T/pip-ephem-wheel-cache-r130htak/wheels/21/7b/57/6a9208d976578979f3fb49f0e6cef344374cd3372e994a873b
Successfully built pyGD
Installing collected packages: six, pyparsing, pygments, pluggy, pillow, packaging, numpy, networkx, kiwisolver, iniconfig, fonttools, cycler, scipy, python-dateutil, pytest, contourpy, matplotlib, pyGD

Successfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.63.0 iniconfig-2.3.0 kiwisolver-1.5.0 matplotlib-3.11.1 networkx-3.6.1 numpy-2.5.2 packaging-26.3 pillow-12.3.0 pluggy-1.6.0 pyGD-0.1.0 pygments-2.21.0 pyparsing-3.3.2 pytest-9.1.1 python-dateutil-2.9.0.post0 scipy-1.18.0 six-1.17.0

$ pytest
============================= test session starts ==============================
platform darwin -- Python 3.14.5, pytest-9.1.1, pluggy-1.6.0
rootdir: /private/tmp/pygd-clean-AwUq9X/pyGD
configfile: pyproject.toml
collected 5 items

tests/test_slice.py .....                                                [100%]

============================== 5 passed in 4.03s ===============================
Evidence: Clean-environment run of the README 'Documentation' recipe (same commands as the CI docs job)

$ pip install -e ".[docs]" Successfully installed ... sphinx-9.1.0 sphinx-copybutton-0.5.2 sphinx-rtd-theme-3.1.0 sphinxcontrib-bibtex-2.7.0 ... $ sphinx-build -b html docs docs/_build/html writing output... [100%] theory generating indices... genindex py-modindex done build succeeded. The HTML pages are in docs/_build/html.

### Following README 'Documentation' recipe verbatim (same commands as the CI docs job)
$ pip install -e ".[docs]"
    Uninstalling pyGD-0.1.0:
      Successfully uninstalled pyGD-0.1.0

Successfully installed Jinja2-3.1.6 MarkupSafe-3.0.3 alabaster-1.0.0 babel-2.18.0 certifi-2026.7.22 charset_normalizer-3.5.1 docutils-0.22.4 idna-3.19 imagesize-2.0.0 latexcodec-3.0.1 pyGD-0.1.0 pybtex-0.26.1 pybtex-docutils-1.0.3 pyyaml-6.0.3 requests-2.34.2 roman-numerals-4.1.0 snowballstemmer-3.1.1 sphinx-9.1.0 sphinx-copybutton-0.5.2 sphinx-rtd-theme-3.1.0 sphinxcontrib-applehelp-2.0.0 sphinxcontrib-bibtex-2.7.0 sphinxcontrib-devhelp-2.0.0 sphinxcontrib-htmlhelp-2.1.0 sphinxcontrib-jquery-4.1 sphinxcontrib-jsmath-1.0.1 sphinxcontrib-qthelp-2.0.0 sphinxcontrib-serializinghtml-2.0.0 urllib3-2.7.0

$ sphinx-build -b html docs docs/_build/html
copying static files: done
copying extra files... 
copying extra files: done
copying assets: done
writing output... [ 10%] api/pyGD
writing output... [ 20%] api/pyGD.agents
writing output... [ 30%] api/pyGD.dynamics
writing output... [ 40%] applet
writing output... [ 50%] getting_started
writing output... [ 60%] guide_dynamics
writing output... [ 70%] guide_yokai
writing output... [ 80%] index
writing output... [ 90%] license
writing output... [100%] theory

generating indices... genindex py-modindex done
highlighting module code... [ 50%] pyGD.agents
highlighting module code... [100%] pyGD.dynamics

writing additional pages... search done
dumping search index in English (code: en)... done
dumping object inventory... done
build succeeded.

The HTML pages are in docs/_build/html.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 3 infos
  • ℹ️ .github/workflows/tests.yml:1 - The workflow declares no permissions: block, so both jobs inherit the repository's default GITHUB_TOKEN scopes (read/write on all scopes for repos created before the default changed). No step needs more than read access to the repo contents. Add a top-level permissions: contents: read after name: Tests; it is a single key and keeps the workflow's deliberately boring shape.
  • ℹ️ .github/workflows/tests.yml:3 - on: push: with no branch filter plus pull_request: means a branch in this repo with an open PR triggers the full 7-entry matrix plus the docs job twice for every push, and with no concurrency group superseded runs keep running to completion. This is the direct consequence of the intent's explicit "every push and every pull request" requirement, so noting the cost only - narrowing the push trigger or adding cancel-in-progress concurrency would change the author's stated behavior and is the user's call.
  • ℹ️ .github/workflows/tests.yml:9 - runs-on: ubuntu-latest is a floating label while the matrix pins Python 3.8, which is past end-of-life. actions/setup-python resolves 3.8 on the current image (verified: run 32224258403 has test (3.8) green), but prebuilt 3.8 artifacts stop being published for newer runner images, so the 3.8 leg is the first thing that breaks when the label rolls forward. No change needed now; the fix at that point is pinning an older runs-on for the oldest matrix entries rather than dropping the claimed version.
✅ **Test** - passed

✅ No issues found.

  • gh-axi run list --commit 69f2cecf6b59085a7613bb514daebb5f47ea3156 - located the real CI run for the target commit
  • gh-axi run view 32224258403 - confirmed 8/8 jobs succeeded (test 3.8-3.14 + docs)
  • gh-axi run view 32224258403 --job <id> --log for all 8 jobs - extracted per-job interpreter version, pytest result, and sphinx build result from the real CI logs
  • Grepped all 8 CI logs for pip install -e ".[test]" / ".[docs]" and for any bare pip install pytest|sphinx - extras used everywhere, no hand-installed test deps
  • Clean-environment reproduction of the README 'Tests' recipe on a fresh copy of the repo outside the worktree: python3 -m venv .venv && pip install -e ".[test]" && pytest -> 5 passed
  • Clean-environment reproduction of the README 'Documentation' recipe (identical to the CI docs job): pip install -e ".[docs]" && sphinx-build -b html docs docs/_build/html -> build succeeded
  • Constraint check script parsing .github/workflows/tests.yml against pyproject.toml: matrix vs classifiers equality, requires-python floor, push + pull_request triggers, fail-fast false, [test]/[docs] extras, docs pinned to 3.12, no cache key, only actions/checkout + actions/setup-python
  • Captured the GitHub Actions run summary page at 1440x1000 via chrome-devtools-axi screenshot
  • git diff --stat 1b72a4e..69f2cec - change scope is exactly one file, .github/workflows/tests.yml
  • git status --porcelain --ignored - worktree clean after testing, no transient artifacts left behind
⚠️ **Document** - 1 info
  • ℹ️ README.md:92 - Judgment call, deliberately left unactioned: README.md now understates its own guarantees - CI verifies the Tests and Documentation install recipes on every push and PR across Python 3.8-3.14, but the README carries no status badge or note saying so. This is an addition rather than a staleness fix, and the change's stated scope was 'only the workflow file - no other repository changes', so I made no edit. Surfacing it so a future maintainer can decide to add a badge; README.md owns the user-facing introduction and would be the single correct home for it.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@EternalTime
EternalTime merged commit 0192c52 into main Aug 19, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant